package org.maxgroundstation.simulation.service;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Handles all TCP service communication.
 */
public class ServiceCommunicationTCP implements Runnable {
	
	// Default TCP port
	private static final int DEFAULT_PORT = 10001;
	
	// Data members
	private ServiceCommunication comm;
    private int port;
    private ServerSocket serverSocket;
    private Thread acceptThread;
    private List<TCPClientSession> clientSessions;
    
    /**
     * Constructor
     * @param comm the service communication.
     */
    ServiceCommunicationTCP(ServiceCommunication comm) {
    	this.comm = comm;
    	port = DEFAULT_PORT;
    	serverSocket = null;
    	acceptThread = null;
    	clientSessions = new ArrayList<TCPClientSession>();
    }
    
    /**
     * Gets the service communication.
     * @return service communication.
     */
    ServiceCommunication getServiceCommunication() {
    	return comm;
    }
    
    /**
     * Gets the current port number.
     * @return port number.
     */
    int getPort() {
        return port;
    }
    
    /**
     * Checks if connected to robot.
     * @return true if connected
     */
    boolean isConnected() {
    	return ((serverSocket != null) && !serverSocket.isClosed());
    }
    
    /**
     * Connects to the TCP communication socket.
     * @throws IOException if error connecting to socket.
     */
    void connect() throws IOException {
    	if (!isConnected()) {
    		serverSocket = new ServerSocket(getPort());
    		System.out.println("Connecting TCP Service on port: " + port);
    		start();
    	}
    }
    
    /**
     * Closes the TCP communication socket.
     * @throws IOException if error closing socket.
     */
    void disconnect() throws IOException {
    	if (isConnected()) {
    		System.out.println("Disconnecting TCP service socket");
    		serverSocket.close();
    		
    		Iterator<TCPClientSession> i = clientSessions.iterator();
    		while (i.hasNext()) {
    			i.next().close();
    		}
    		clientSessions.clear();
    	}
    }
    
    private void start() {
    	if ((acceptThread == null) || !acceptThread.isAlive()) {
        	acceptThread = new Thread(this, "tcp accept thread");
        	acceptThread.start();
        }
    }
    
	@Override
	public void run() {
		while (isConnected()) {
			try {
				comm.log("TCP service waiting for sockets");
				Socket socket = serverSocket.accept();
				
				comm.log("TCP service accepting socket");
				
				TCPClientSession clientSession = new TCPClientSession(comm, socket);
				clientSessions.add(clientSession);
			}
			catch(IOException e) {
				//e.printStackTrace(System.err);
			}
		}
	}
	
	private TCPClientSession getClientSession(String clientID) {
		TCPClientSession result = null;
		
		Iterator<TCPClientSession> i = clientSessions.iterator();
		while(i.hasNext()) {
			TCPClientSession clientSession = i.next();
			if (!clientSession.isClosed()) {
				if (clientID.equals(clientSession.getClientID())) result = clientSession;
			}
			else {
				i.remove();
			}
		}
		
		return result;
	}
	
    public void sendServicePost(String serviceID, String clientID, byte[] data) throws Exception {
    	TCPClientSession clientSession = getClientSession(clientID);
    	if (clientSession != null) {
    		//System.out.println("Sending TCP service: " + serviceID + " to client: " + clientID);
    		clientSession.sendServicePost(serviceID, data);
    	}
    	else {
    		System.out.println("TCP session for client: " + clientID + " not found.");
    		throw new Exception("TCP session for client: " + clientID + " not found.");
    	}
    }
}